#include "skinPainterContextCmd.h"

#include "skinPainterContext.h"

#include <maya/MArgList.h>
#include <maya/MGlobal.h>
#include <maya/MSelectionList.h>
#include <maya/MItMeshPolygon.h>
#include <maya/MIntArray.h>
#include <maya/MPlug.h>



struct CmdFlagObj
{
	MString flagShortName;
	MString flagLongName;
	MSyntax::MArgType flagType;

	CmdFlagObj(const MString& flagShort, const MString& flagLong, const MSyntax::MArgType& flagtype):
	flagShortName(flagShort), flagLongName(flagLong), flagType(flagtype) {};
};


static CmdFlagObj Flag_BrushColor("-bc", "-brushColor", MSyntax::MArgType::kDouble);
static CmdFlagObj Flag_DrawMesh("-m", "-mesh", MSyntax::MArgType::kString);
static CmdFlagObj Flag_JointIndex("-ji", "-jointIndex", MSyntax::MArgType::kUnsigned);
static CmdFlagObj Flag_UpdateInfluenceJoint("-uij", "-updateInfluenceJoint", MSyntax::MArgType::kBoolean);
static CmdFlagObj Flag_BrushOpacity("-opa", "-opacity", MSyntax::MArgType::kDouble);
static CmdFlagObj Flag_BrushValue("-v", "-value", MSyntax::MArgType::kDouble);
static CmdFlagObj Flag_PaintOperation("-po", "-paintOperation", MSyntax::MArgType::kString);

MString skinPainterContextCmd::cmdName = "xsSkinBrush";

skinPainterContextCmd::skinPainterContextCmd()
{
}

skinPainterContextCmd::~skinPainterContextCmd()
{
}

MPxContext* skinPainterContextCmd::makeObj()
{
	if (!skinPainterCtx)
	{
		skinPainterCtx = new skinPainterContext();
	}
	
	return skinPainterCtx;
}

MStatus skinPainterContextCmd::doEditFlags()
{
	MArgParser argData = parser();

	const char* flagBrushColor = Flag_BrushColor.flagShortName.asChar();
	const char* flagDrawMesh = Flag_DrawMesh.flagShortName.asChar();
	const char* flagJointIndex = Flag_JointIndex.flagShortName.asChar();
	const char* flagUpdateInfluenceJoint = Flag_UpdateInfluenceJoint.flagShortName.asChar();
	const char* flagOpacity = Flag_BrushOpacity.flagShortName.asChar();
	const char* flagValue = Flag_BrushValue.flagShortName.asChar();
	const char* flagPaintOperation = Flag_PaintOperation.flagShortName.asChar();

	// update brush color
	if (argData.isFlagSet(flagBrushColor))
	{
		double rgb[3];
		unsigned int colormem = argData.numberOfFlagUses(flagBrushColor);
		for (unsigned int i = 0; i < colormem; ++i)
		{
			if (i >= 3)
				break;
			
			MArgList argList;
			argData.getFlagArgumentList(flagBrushColor, i, argList);
			
			rgb[i] = argList.asDouble(0);
		}

		skinPainterCtx->SetBrushColor(rgb[0], rgb[1], rgb[2]);
	}

	// update mesh
	if (argData.isFlagSet(flagDrawMesh))
	{
		MString drawMeshName;
		argData.getFlagArgument(flagDrawMesh, 0, drawMeshName);

		MSelectionList mSelListl;
		mSelListl.add(drawMeshName);

		MDagPath dagpathMeshShape;
		mSelListl.getDagPath(0, dagpathMeshShape);
		if (dagpathMeshShape.extendToShape() == MStatus::kSuccess)
		{
			// uodate draw mesh
			skinPainterCtx->SetDrawMesh(dagpathMeshShape);

			// update mesh vtx index
			//UpdateMeshVtxIndices(dagpathMeshShape);

			if (argData.isFlagSet(flagUpdateInfluenceJoint))
			{
				UpdateInfluenceJnt(dagpathMeshShape);
			}
		}
	}

	// uppdate joint index
	if (argData.isFlagSet(flagJointIndex))
	{
		unsigned int jointIdx = 0;
		argData.getFlagArgument(flagJointIndex, 0, jointIdx);
		skinPainterCtx->SetPaintJointIndex(jointIdx);
	}

	// opacity
	if (argData.isFlagSet(flagOpacity))
	{
		double opacityVal = 0.0;
		argData.getFlagArgument(flagOpacity, 0, opacityVal);

		skinPainterCtx->SetBrushOpacity(opacityVal);
	}

	// value
	if (argData.isFlagSet(flagValue))
	{
		double val = 0.0;
		argData.getFlagArgument(flagValue, 0, val);
		skinPainterCtx->SetBrushValue(val);
	}
	
	// paint operation
	if (argData.isFlagSet(flagPaintOperation))
	{
		MString strPaintOpe;
		argData.getFlagArgument(flagPaintOperation, 0, strPaintOpe);

		if (strPaintOpe == "add")
			skinPainterCtx->SetPaintOperation(PaintOperation::kAdd);
		else if (strPaintOpe == "replace")
			skinPainterCtx->SetPaintOperation(PaintOperation::kReplace);
		else if (strPaintOpe == "smooth")
			skinPainterCtx->SetPaintOperation(PaintOperation::kSmooth);
	}
	
	return MStatus::kSuccess;
}

MStatus skinPainterContextCmd::appendSyntax()
{
	MSyntax mSyntax = syntax();

	mSyntax.addFlag(Flag_BrushColor.flagShortName.asChar(), Flag_BrushColor.flagLongName.asChar(), Flag_BrushColor.flagType);
	mSyntax.makeFlagMultiUse(Flag_BrushColor.flagShortName.asChar());
	
	mSyntax.addFlag(Flag_DrawMesh.flagShortName.asChar(), Flag_DrawMesh.flagLongName.asChar(), Flag_DrawMesh.flagType);
	mSyntax.addFlag(Flag_JointIndex.flagShortName.asChar(), Flag_JointIndex.flagLongName.asChar(), Flag_JointIndex.flagType);
	mSyntax.addFlag(Flag_UpdateInfluenceJoint.flagShortName.asChar(), Flag_UpdateInfluenceJoint.flagLongName.asChar(), Flag_UpdateInfluenceJoint.flagType);
	mSyntax.addFlag(Flag_BrushOpacity.flagShortName.asChar(), Flag_BrushOpacity.flagLongName.asChar(), Flag_BrushOpacity.flagType);
	mSyntax.addFlag(Flag_PaintOperation.flagShortName.asChar(), Flag_PaintOperation.flagLongName.asChar(), Flag_PaintOperation.flagType);
	mSyntax.addFlag(Flag_BrushValue.flagShortName.asChar(), Flag_BrushValue.flagLongName.asChar(), Flag_BrushValue.flagType);
	
	return MStatus::kSuccess;
}

void* skinPainterContextCmd::creator()
{
	return new skinPainterContextCmd();
}

// void skinPainterContextCmd::UpdateMeshVtxIndices(const MDagPath& mesh)
// {
// 	MItMeshPolygon mitMeshPoly(mesh);
//
// 	MIntArray triangleIndices;
// 	MIntArray triangleStripIndices;
// 	std::vector<MIntArray> wireframeIndices;
//
// 	for (; !mitMeshPoly.isDone(); mitMeshPoly.next())
// 	{
// 		MIntArray vtxIndices;
// 		mitMeshPoly.getVertices(vtxIndices);
//
// 		// line frame indices
// 		wireframeIndices.emplace_back(vtxIndices);
//
// 		unsigned int numVtx = mitMeshPoly.polygonVertexCount();
// 		// if is triangle or not
// 		if (numVtx == 3)
// 		{
// 			for (auto idx:vtxIndices)
// 				triangleIndices.append(idx);
// 		}
// 		else
// 		{
// 			SplitTriangleVtxRecursive(vtxIndices, triangleStripIndices);
// 		}
// 	}
//
// 	skinPainterCtx->SetVtxIndices(triangleIndices, triangleStripIndices, wireframeIndices);
// }

void skinPainterContextCmd::SplitTriangleVtxRecursive(MIntArray inIndices, MIntArray& outIndices)
{
	for (int i = 0; i < 3; ++i)
	{
		outIndices.append(inIndices[i]);
	}

	if (inIndices.length() == 3)
		return;
	else
	{
		inIndices.remove(1);
		SplitTriangleVtxRecursive(inIndices, outIndices);
	}
}

void skinPainterContextCmd::UpdateInfluenceJnt(const MDagPath& mesh)
{
	MFnDependencyNode mfndpMesh(mesh.node());
	MPlug plugInMesh = mfndpMesh.findPlug("inMesh", false);
	MFnDependencyNode mfndpDeformer(plugInMesh.source().node());

	MIntArray influenceIndices;
	MPlug plugInfluence = mfndpDeformer.findPlug("influenceMatrices", false);
	unsigned int numInfluence = plugInfluence.numElements();
	for (unsigned int i = 0; i < numInfluence; ++i)
	{
		MPlug influence_i = plugInfluence.elementByLogicalIndex(i);
		if (influence_i.isDestination())
		{
			MFnDependencyNode mfnDPJnt(influence_i.source().node());
			bool isLockWeight = mfnDPJnt.findPlug("lockInfluenceWeights", false).asBool();
			if (!isLockWeight)
			{
				influenceIndices.append(i);
			}
		}
	}

	skinPainterCtx->UpdateInfluenceJnt(influenceIndices);
	skinPainterCtx->SetInfluenceJointNum(numInfluence);
}
